﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Collections;
using System.Diagnostics;
using System.Windows.Forms;

namespace WindowPlace.Common
{
    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }

    public static class Helper
    {
        const int MAXTITLE = 255;

        private static ArrayList mTitlesList;
        private static ArrayList mWindowInfosList;

        private delegate bool EnumDelegate(IntPtr hWnd, int lParam);

        #region DLL Imports

        [DllImport("user32.dll", EntryPoint = "EnumDesktopWindows",
         ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool _EnumDesktopWindows(IntPtr hDesktop,
        EnumDelegate lpEnumCallbackFunction, IntPtr lParam);

        [DllImport("user32.dll", EntryPoint = "GetWindowText",
         ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int _GetWindowText(IntPtr hWnd,
        StringBuilder lpWindowText, int nMaxCount);

        [DllImport("user32.dll", SetLastError = true)]
        static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern bool IsWindowVisible(IntPtr hWnd);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

        /// <summary> The RegisterHotKey function defines a system-wide hot key </summary>
        /// <param name="hwnd">Handle to the window that will receive WM_HOTKEY messages  generated by the hot key.</param>
        /// <param name="id">Specifies the identifier of the hot key.</param>
        /// <param name="fsModifiers">Specifies keys that must be pressed in combination with the key  specified by the 'vk' parameter in order to generate the WM_HOTKEY message.</param>
        /// <param name="vk">Specifies the virtual-key code of the hot key</param>
        /// <returns><c>true</c> if the function succeeds, otherwise <c>false</c></returns>
        /// <seealso cref="http://msdn.microsoft.com/en-us/library/ms646309(VS.85).aspx"/>
        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);

        [DllImport("user32.dll")]
        public  static extern bool UnregisterHotKey(IntPtr hWnd, int id);

        /// <summary>
        /// The MoveWindow function changes the position and dimensions of the specified window. For a top-level window, the position and dimensions are relative to the upper-left corner of the screen. For a child window, they are relative to the upper-left corner of the parent window's client area.
        /// </summary>
        /// <param name="hWnd">Handle to the window.</param>
        /// <param name="X">Specifies the new position of the left side of the window.</param>
        /// <param name="Y">Specifies the new position of the top of the window.</param>
        /// <param name="nWidth">Specifies the new width of the window.</param>
        /// <param name="nHeight">Specifies the new height of the window.</param>
        /// <param name="bRepaint">Specifies whether the window is to be repainted. If this parameter is TRUE, the window receives a message. If the parameter is FALSE, no repainting of any kind occurs. This applies to the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent window uncovered as a result of moving a child window.</param>
        /// <returns>If the function succeeds, the return value is nonzero.
        /// <para>If the function fails, the return value is zero. To get extended error information, call GetLastError.</para></returns>
        [DllImport("user32.dll", SetLastError = true)]
        internal static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

        /// <summary>
        /// Retrieves the fully-qualified path for the file containing the specified module.
        /// http://msdn.microsoft.com/en-us/library/ms683198(VS.85).aspx
        /// </summary>
        /// <param name="hProcess"></param>
        /// <param name="hModule"></param>
        /// <param name="lpBaseName"></param>
        /// <param name="nSize"></param>
        /// <returns></returns>
        [DllImport("psapi.dll")] //Supported under Windows Vista and Windows Server 2008. 
        static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName,
        [In] [MarshalAs(UnmanagedType.U4)] int nSize);

        /// <summary>
        /// Retrieves the identifier of the thread that created the specified window and, optionally, the identifier of the process that created the window. 
        /// http://msdn.microsoft.com/en-us/library/ms633522(VS.85).aspx
        /// </summary>
        /// <param name="handle"></param>
        /// <param name="processId"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int GetWindowThreadProcessId(IntPtr handle, out uint processId);

        /// <summary>Shows a Window</summary>
        /// <remarks>
        /// <para>To perform certain special effects when showing or hiding a 
        /// window, use AnimateWindow.</para>
        ///<para>The first time an application calls ShowWindow, it should use 
        ///the WinMain function's nCmdShow parameter as its nCmdShow parameter. 
        ///Subsequent calls to ShowWindow must use one of the values in the 
        ///given list, instead of the one specified by the WinMain function's 
        ///nCmdShow parameter.</para>
        ///<para>As noted in the discussion of the nCmdShow parameter, the 
        ///nCmdShow value is ignored in the first call to ShowWindow if the 
        ///program that launched the application specifies startup information 
        ///in the structure. In this case, ShowWindow uses the information 
        ///specified in the STARTUPINFO structure to show the window. On 
        ///subsequent calls, the application must call ShowWindow with nCmdShow 
        ///set to SW_SHOWDEFAULT to use the startup information provided by the 
        ///program that launched the application. This behavior is designed for 
        ///the following situations: </para>
        ///<list type="">
        ///    <item>Applications create their main window by calling CreateWindow 
        ///    with the WS_VISIBLE flag set. </item>
        ///    <item>Applications create their main window by calling CreateWindow 
        ///    with the WS_VISIBLE flag cleared, and later call ShowWindow with the 
        ///    SW_SHOW flag set to make it visible.</item>
        ///</list></remarks>
        /// <param name="hWnd">Handle to the window.</param>
        /// <param name="nCmdShow">Specifies how the window is to be shown. 
        /// This parameter is ignored the first time an application calls 
        /// ShowWindow, if the program that launched the application provides a 
        /// STARTUPINFO structure. Otherwise, the first time ShowWindow is called, 
        /// the value should be the value obtained by the WinMain function in its 
        /// nCmdShow parameter. In subsequent calls, this parameter can be one of 
        /// the WindowShowStyle members.</param>
        /// <returns>
        /// If the window was previously visible, the return value is nonzero. 
        /// If the window was previously hidden, the return value is zero.
        /// </returns>
        [DllImport("user32.dll", EntryPoint = "ShowWindow")]
        private static extern bool _ShowWindow(IntPtr hWnd, WindowShowStyle nCmdShow);

#endregion

        public static Int32 LoWord(Int32 val) { return val & 0xffff; }
        // Uses LoWord to prevent sign propagation
        public static Int32 HiWord(Int32 val) { return LoWord(val >> 16); }


        private static bool EnumWindowsProc(IntPtr hWnd, int lParam)
        {
            WindowInfo wi = new WindowInfo();

            wi.Caption = GetWindowText(hWnd);
            wi.hWnd = hWnd;
            wi.IsVisible = GetWindowIsVisible(hWnd);
            wi.CurrentRectangle = GetWindowRectangle(hWnd);
            wi.ModuleFullFilename = GetProcessPath(hWnd);
            mWindowInfosList.Add(wi);
            mTitlesList.Add(wi.Caption);
            return true;
        }

        /// <summary>
        /// Returns the caption of a window by the given HWND identifier.
        /// </summary>
        public static string GetWindowText(IntPtr hWnd)
        {
            StringBuilder title = new StringBuilder(MAXTITLE);
            int titleLength = _GetWindowText(hWnd, title, title.Capacity + 1);
            title.Length = titleLength;
            return title.ToString();
        }

        /// <summary>
        /// Returns the RECT of a window by the given HWND identifier.
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        public static RECT GetWindowRectangle(IntPtr hWnd)
        {
            RECT r;
            GetWindowRect(hWnd, out r);
            return r;
        }

        /// <summary>
        /// Returns True if a window, given by the HWND identifier, is visible
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        public static bool GetWindowIsVisible(IntPtr hWnd)
        {
            return IsWindowVisible(hWnd);
        }

        /// <summary>
        /// Repositions and Resizes a window
        /// </summary>
        /// <param name="hWnd"></param>
        /// <param name="Rectangle"></param>
        /// <returns>Returns True is succesfull</returns>
        public static bool SetWindowRectangle(IntPtr hWnd, RECT Rectangle)
        {
            return MoveWindow(hWnd, Rectangle.Left, Rectangle.Top, Rectangle.Right - Rectangle.Left, Rectangle.Bottom - Rectangle.Top, true);
        }

        public static WindowInfo[] GetWindowInfos()
        {
            mTitlesList = new ArrayList();
            mWindowInfosList = new ArrayList();
            EnumDelegate enumfunc = new EnumDelegate(EnumWindowsProc);
            IntPtr hDesktop = IntPtr.Zero; // current desktop
            bool success = _EnumDesktopWindows(hDesktop, enumfunc, IntPtr.Zero);
            if (success)
            {
                // Copy the result to string array
                WindowInfo[] infos = new WindowInfo[mWindowInfosList.Count];

                mWindowInfosList.CopyTo(infos);
                return infos;
            }
            else
            {
                // Get the last Win32 error code
                int errorCode = Marshal.GetLastWin32Error();

                string errorMessage = String.Format(
                "EnumDesktopWindows failed with code {0}.", errorCode);
                throw new Exception(errorMessage);
            }
        }

        /// <summary>
        /// Returns the caption of all desktop windows.
        /// </summary>
        public static string[] GetDesktopWindowsCaptions()
        {
            mTitlesList = new ArrayList();
            EnumDelegate enumfunc = new EnumDelegate(EnumWindowsProc);
            IntPtr hDesktop = IntPtr.Zero; // current desktop
            bool success = _EnumDesktopWindows(hDesktop, enumfunc, IntPtr.Zero);

            if (success)
            {
                // Copy the result to string array
                string[] titles = new string[mTitlesList.Count];
                mTitlesList.CopyTo(titles);
                return titles;
            }
            else
            {
                // Get the last Win32 error code
                int errorCode = Marshal.GetLastWin32Error();

                string errorMessage = String.Format(
                "EnumDesktopWindows failed with code {0}.", errorCode);
                throw new Exception(errorMessage);
            }
        }

        /// <summary>
        /// Retrieves the Path of a running process. 
        /// </summary>
        /// <param name="hwnd"></param>
        /// <returns></returns>
        public static string GetProcessPath(IntPtr hwnd)
        {
            try
            {
                uint pid = 0;
                GetWindowThreadProcessId(hwnd, out pid);
                Process proc = Process.GetProcessById((int)pid); //Gets the process by ID. 
                return proc.MainModule.FileName.ToString();    //Returns the path. 
            }
            catch (Exception ex)
            {
                return ex.Message.ToString();
            }
        }

        /// <summary>
        /// Shows the given Window using the given WindowShowStyle
        /// </summary>
        /// <param name="hWnd"></param>
        /// <param name="Style"></param>
        /// <returns></returns>
        public static bool ShowWindow(IntPtr hWnd, WindowShowStyle Style)
        {
            return _ShowWindow(hWnd, Style);
        }

        public static int NumericKeyToInteger(int KeyCode)
        {
            Keys k = (Keys)KeyCode;
            if (k == Keys.D1) return 1;
            else if (k == Keys.D2) return 2;
            else if (k == Keys.D3) return 3;
            else if (k == Keys.D4) return 4;
            else if (k == Keys.D5) return 5;
            else if (k == Keys.D6) return 6;
            else if (k == Keys.D7) return 7;
            else if (k == Keys.D8) return 8;
            else if (k == Keys.D9) return 9;
            else return -1;
        }

        public static Keys IntegerToKey(int Value)
        {
            if (Value == 0) return Keys.D0;
            else if (Value == 1) return Keys.D1;
            else if (Value == 2) return Keys.D2;
            else if (Value == 3) return Keys.D3;
            else if (Value == 4) return Keys.D4;
            else if (Value == 5) return Keys.D5;
            else if (Value == 6) return Keys.D6;
            else if (Value == 7) return Keys.D7;
            else if (Value == 8) return Keys.D8;
            else if (Value == 9) return Keys.D9;

            else return Keys.D0;

        }
    
    }
}
